import numpy as np
import random
from PIL import Image
import math


class Write_Actor:  # 写数据类
    limit = (0, 255)
    rand_seed = 0
    pic_data = None
    pic_size = [0, 0]
    idx = []

    def __init__(self, pic_data, limit, rand_seed):
        self.limit = limit  # 约束
        self.pic_data = np.array(pic_data)  # 图片数据
        self.rand_seed = rand_seed  # 随机数种子
        self.pic_size[0] = self.pic_data.shape[0]  # 图片尺寸
        self.pic_size[1] = self.pic_data.shape[1]

    def get_id(self, data_len):  # 获取编辑序列
        cnt = 0  # 统计生成了多少个序列
        idx = []  # 存储下标
        random.seed(self.rand_seed)  # 使用随机数种子，确保前后一致
        self.idx.clear()  # 清空下表列表
        while cnt < data_len:
            tmp_idx = random.randint(
                0, self.pic_size[0] * self.pic_size[1] - 1
            )  # 生成随机数
            if tmp_idx not in idx:  # 检测是否在禁用下表内
                idx_i, idx_j = self.get_ij(tmp_idx)  # 转化为二维下表
                if self.check_ij(idx_i, idx_j):  # 检测像素块是否可选
                    idx.append(tmp_idx)  # 添加入禁用列表
                    cnt = cnt + 1
                    idx_i, idx_j = self.get_ij(tmp_idx)  # 导入列表
                    self.idx.append((idx_i, idx_j))
                    idx = idx + self.get_relate_num(idx_i, idx_j)  # 补充禁用下标

    def write_data(self, data_list):
        self.idx.clear()
        self.get_id(len(data_list))
        # print(self.idx)
        for i in range(len(data_list)):
            self.write_ij(self.idx[i][0], self.idx[i][1], data_list[i])

    def get_relate_num(
        self, idx_i, idx_j
    ):  # 为防止重复处理某两个像素块，获取他的边缘块加入检测
        ans_list = []
        if idx_j == 0:  # 某行第一个
            ans_list.append(self.ij_num(idx_i, 1))  # 加入右侧
        else:
            ans_list.append(self.ij_num(idx_i, idx_j - 1))  # 否则前后加入
            ans_list.append(self.ij_num(idx_i, idx_j + 1))
        return ans_list

    def ij_num(self, i, j):
        return self.pic_size[0] * i + j

    def get_ij(self, num):  # 转化下标
        i = num // self.pic_size[0]
        j = num % self.pic_size[1]
        return i, j

    def check_ij(self, i, j):  # 检测可否嵌入
        if (
            i < 0 or i >= self.pic_size[0] - 1 or j < 0 or j >= self.pic_size[1] - 1
        ):  # 数组越界
            return False
        if self.pic_data[i][j] > self.pic_data[i][j + 1]:
            d = self.pic_data[i][j] - self.pic_data[i][j + 1]
        else:
            d = self.pic_data[i][j + 1] - self.pic_data[i][j]
        if d < self.limit[0] or d > self.limit[1]:  # 超出范围
            return False
        return True

    def write_ij(self, i, j, m):
        op = m  # 加入嵌入数值，计算新的差值
        od = int(op / 2)  # 二分
        base_data = self.limit[0] + od
        k = 0
        if self.pic_data[i][j] > self.pic_data[i][j + 1]:
            k = self.pic_data[i][j] - self.pic_data[i][j + 1]
        else:
            k = self.pic_data[i][j + 1] - self.pic_data[i][j]
        min_d = base_data - od
        max_d = base_data + od
        if max_d > 200:
            delta = max_d - 200
            max_d = 200
            min_d = min_d - delta
        if min_d < 0:
            delta = 1 - min_d
            min_d = 1
            max_d = max_d + delta

        if self.pic_data[i][j] < self.pic_data[i][j + 1]:
            self.pic_data[i][j] = min_d  # 均匀分入图片
            self.pic_data[i][j + 1] = max_d
            if m % 2 == 1:
                self.pic_data[i][j + 1] = max_d + 1
        else:
            self.pic_data[i][j + 1] = min_d  # 均匀分入图片
            self.pic_data[i][j] = max_d
            if m % 2 == 1:
                self.pic_data[i][j] = max_d + 1
        h = 0
        if self.pic_data[i][j] > self.pic_data[i][j + 1]:
            h = self.pic_data[i][j] - self.pic_data[i][j + 1]
        else:
            h = self.pic_data[i][j + 1] - self.pic_data[i][j]
        if h < self.limit[0] or h < self.limit[0] > self.limit[1]:
            print("err", h, m, k)
            print(min_d, max_d, self.pic_data[i][j], self.pic_data[i][j + 1])
            print()

    def get_pic(self):  # 返回结果图片
        return self.pic_data


class Read_Actor:  # 写数据类
    limit = (0, 255)
    rand_seed = 0
    pic_data = None
    pic_size = [0, 0]
    idx = []
    data_list = []

    def __init__(self, pic_data, limit, rand_seed):
        self.limit = limit  # 约束
        self.pic_data = np.array(pic_data)  # 图片数据
        self.rand_seed = rand_seed  # 随机数种子
        self.pic_size[0] = self.pic_data.shape[0]  # 图片尺寸
        self.pic_size[1] = self.pic_data.shape[1]

    def get_id(self, data_len):  # 获取编辑序列
        cnt = 0  # 统计生成了多少个序列
        idx = []  # 存储下标
        random.seed(self.rand_seed)  # 使用随机数种子，确保前后一致
        self.idx.clear()  # 清空下表列表
        while cnt < data_len:
            tmp_idx = random.randint(
                0, self.pic_size[0] * self.pic_size[1] - 1
            )  # 生成随机数
            if tmp_idx not in idx:  # 检测是否在禁用下表内
                idx_i, idx_j = self.get_ij(tmp_idx)  # 转化为二维下表
                if self.check_ij(idx_i, idx_j):  # 检测像素块是否可选
                    idx.append(tmp_idx)  # 添加入禁用列表
                    cnt = cnt + 1
                    idx_i, idx_j = self.get_ij(tmp_idx)  # 导入列表
                    self.idx.append((idx_i, idx_j))
                    idx = idx + self.get_relate_num(idx_i, idx_j)  # 补充禁用下标

    def get_relate_num(
        self, idx_i, idx_j
    ):  # 为防止重复处理某两个像素块，获取他的边缘块加入检测
        ans_list = []
        if idx_j == 0:  # 某行第一个
            ans_list.append(self.ij_num(idx_i, 1))  # 加入右侧
        else:
            ans_list.append(self.ij_num(idx_i, idx_j - 1))  # 否则前后加入
            ans_list.append(self.ij_num(idx_i, idx_j + 1))
        return ans_list

    def ij_num(self, i, j):
        return self.pic_size[0] * i + j

    def get_ij(self, num):  # 转化下标
        i = num // self.pic_size[0]
        j = num % self.pic_size[1]
        return i, j

    def check_ij(self, i, j):  # 检测可否嵌入
        if (
            i < 0 or i >= self.pic_size[0] - 1 or j < 0 or j >= self.pic_size[1] - 1
        ):  # 数组越界
            return False
        d = 0
        if self.pic_data[i][j] > self.pic_data[i][j + 1]:
            d = self.pic_data[i][j] - self.pic_data[i][j + 1]
        else:
            d = self.pic_data[i][j + 1] - self.pic_data[i][j]
        if d < self.limit[0] or d > self.limit[1]:  # 超出范围
            return False
        return True

    def read_data(self, data_len):
        self.idx.clear()
        self.data_list.clear()
        self.get_id(data_len)
        for i in range(data_len):
            self.data_list.append(self.read_ij(self.idx[i][0], self.idx[i][1]))

    def read_ij(self, i, j):  # 获取数据
        m = abs(int(self.pic_data[i][j]) - int(self.pic_data[i][j + 1]))
        return m

    def get_data(self):  # 返回结果数据
        return self.data_list

# def to_ascii(string):
#     ascii_list = []
#     for char in string:
#         ascii_value = ord(char)
#         ascii_list.append(ascii_value)
#     return ascii_list
#
#
# def from_ascii(ascii_list):
#     string = ""
#     for ascii_value in ascii_list:
#         char = chr(ascii_value)
#         string += char
#     return string

def to_utf8_bytes(string):
    return list(string.encode("utf-8"))


def from_utf8_bytes(byte_list):
    return bytes(byte_list).decode("utf-8")


def split_into_binary_string(numbers, n):
    binary_strings = [
        format(num, "0" + str(n) + "b") for num in numbers
    ]  # 转换为 n 位二进制字符串
    binary_chunks = [
        binary_strings[i : i + n] for i in range(0, len(binary_strings), n)
    ]  # 按照 n 个数字分割
    return "".join(
        "".join(chunk) for chunk in binary_chunks
    )  # 合并所有分割后的二进制字符串


def combine_from_binary_string(binary_string, n):
    binary_chunks = [
        binary_string[i : i + n] for i in range(0, len(binary_string), n)
    ]  # 按照 n 个数字分割
    check = len(binary_chunks[-1])
    if n - check > 0:
        for i in range(n - check):
            binary_chunks[-1] = binary_chunks[-1] + "0"
    numbers = [int(chunk, 2) for chunk in binary_chunks]  # 将二进制字符串转换为整数
    return numbers


def conver_data2_b(data, n):
    binary_string = split_into_binary_string(data, 8)
    reconstructed_numbers = combine_from_binary_string(binary_string, n)
    return reconstructed_numbers


def conver_data2_8b(bits, n):
    binary_string = split_into_binary_string(bits, n)
    if len(binary_string) % 8 != 0:
        n = len(binary_string) // 8
        d = ""
        for i in range(8 * n):
            d = d + binary_string[i]
        binary_string = d
    reconstructed_numbers = combine_from_binary_string(binary_string, 8)
    return reconstructed_numbers


def actor(path, data_r, fw):
    data = Image.open(path)
    r, g, b = data.split()
    print(g)
    data = r
    data = np.array(data)
    a = Write_Actor(data, fw, 0)
    data_w = data_r
    # rsa修改
    l = fw[1] - fw[0] + 1
    n = int(math.log2(l))
    data_w = conver_data2_b(data_w, n)
    for i in range(len(data_w)):
        data_w[i] = data_w[i] + fw[0]
    print(data_w)
    a.write_data(data_w)
    print("ok")
    r = a.get_pic()
    r = r.astype("uint8")
    r = Image.fromarray(r, "L")
    print(r)
    image = Image.merge("RGB", (r, g, b))
    print("ok1")
    image.save("ans.png")
    return len(data_w)


def reader(path, len_data, fw):
    data = Image.open(path)
    r, g, b = data.split()
    data = r
    data = np.array(data)
    b = Read_Actor(data, fw, 0)
    l = fw[1] - fw[0] + 1
    n = int(math.log2(l))
    b.read_data(len_data)
    s = b.get_data()
    for i in range(len(s)):
        s[i] = s[i] - fw[0]
    s = conver_data2_8b(s, n)
    return s


if __name__ == "__main__":

    # [(161, 262), (176, 213), (17, 2), (108, 240),
    # (214, 245), (204, 43), (170, 35), (127, 131),
    # (200, 68), (150, 130)]
    #
    # [(161, 262), (176, 213), (17, 2), (108, 240),
    # (214, 245), (204, 43), (170, 35), (127, 131),
    # (200, 68), (150, 130)]

    data = Image.open("LN.jpg")
    data = data.convert("L")
    data = np.array(data)
    a = Write_Actor(data, (32, 63), 0)
    data_w = "hello word !"
    data_w = to_ascii(data_w)
    data_w = conver_data2_b(data_w, 5)
    d = conver_data2_8b(data_w, 5)
    print(d)
    data_g = from_ascii(d)
    print(data_g)
    for i in range(len(data_w)):
        data_w[i] = data_w[i] + 32
    print(data_w)
    data_len = len(data_w)
    a.write_data(data_w)
    print(a.idx)
    image_1 = a.get_pic()
    image = Image.fromarray(image_1, "L")
    image.save("LN1.png")

    data = Image.open("LN1.png")
    data = np.array(data)
    b = Read_Actor(data, (32, 63), 0)
    b.read_data(data_len)
    print(b.idx)
    s = b.get_data()
    print(s)
    for i in range(len(s)):
        s[i] = s[i] - 32
    print(s)
    s = conver_data2_8b(s, 5)
    data_g = from_ascii(s)
    print(data_g)

# 88 88
# 107 85
# 11 53
# 55 77
# 63 19
# 145 133
# 89 211
# 62 40
# 163 149
# 155 200

# 88 88
# 107 85
# 11 53
# 55 77
# 63 19
# 145 133
# 89 211
# 62 40
# 163 149
# 155 200
